package io.noties.markwon.base.html.jsoup.nodes

import io.noties.markwon.base.html.jsoup.helper.Validate
import java.lang.RuntimeException

/**
 * A single key + value attribute. (Only used for presentation.)
 */
class Attribute(var keyEntry: String, var  valueEntry: String, var parent: Attributes?) :
    MutableMap.MutableEntry<String, String>, Cloneable {
    //    private static final String[] booleanAttributes = {
    //            "allowfullscreen", "async", "autofocus", "checked", "compact", "declare", "default", "defer", "disabled",
    //            "formnovalidate", "hidden", "inert", "ismap", "itemscope", "multiple", "muted", "nohref", "noresize",
    //            "noshade", "novalidate", "nowrap", "open", "readonly", "required", "reversed", "seamless", "selected",
    //            "sortable", "truespeed", "typemustmatch"
    //    };
    //override var key: String

    /**
     * Get the attribute value.
     * @return the attribute value
     */
    //override var value: String
        //private set
    // used to update the holding Attributes when the key / value is changed via this interface
    //var parent   : Attributes?

    override val key: String
        get() = keyEntry
    override val value: String
        get() = valueEntry

    /**
     * Create a new attribute from unencoded (raw) key and value.
     * @param key attribute key; case is preserved.
     * @param value attribute value
     */
    constructor(key: String, value: String) : this(key, value, null) {}

    /**
     * Create a new attribute from unencoded (raw) key and value.
     * @param key attribute key; case is preserved.
     * @param val attribute value
     * @param parent the containing Attributes (this Attribute is not automatically added to said Attributes)
     */
    init {
        Validate.notNull(keyEntry)
        this.keyEntry = keyEntry.trim { it <= ' ' }
        Validate.notEmpty(keyEntry) // trimming could potentially make empty, so validate here
    }


    /**
     * Set the attribute key; case is preserved.
     * @param key the new key; must not be null
     */
    fun setKey(key1: String) {
        var key = key1
        Validate.notNull(key)
        key = key.trim { it <= ' ' }
        Validate.notEmpty(key) // trimming could potentially make empty, so validate here
        if (parent != null) {
            val i = parent!!.indexOfKey(this.keyEntry)
            if (i != Attributes.NotFound) parent!!.keys[i] = key
        }
        this.keyEntry = key
    }

    /**
     * Set the attribute value.
     * @param val the new attribute value; must not be null
     */
    override fun setValue(newValue: String): String {
        val oldVal = parent?.get(keyEntry)?:""
        if (parent != null) {
            val i = parent!!.indexOfKey(keyEntry)
            if (i != Attributes.NotFound) parent!!.vals[i] = newValue
        }
        valueEntry = newValue
        return oldVal
    }

    //    /**
    //     Get the HTML representation of this attribute; e.g. {@code href="index.html"}.
    //     @return HTML
    //     */
    //    public String html() {
    //        StringBuilder accum = new StringBuilder();
    //
    //        try {
    //            html(accum, (new Document("")).outputSettings());
    //        } catch(IOException exception) {
    //            throw new SerializationException(exception);
    //        }
    //        return accum.toString();
    //    }
    //
    //    protected static void html(String key, String val, Appendable accum, Document.OutputSettings out) throws IOException {
    //        accum.append(key);
    //        if (!shouldCollapseAttribute(key, val, out)) {
    //            accum.append("=\"");
    //            Entities.escape(accum, Attributes.checkNotNull(val) , out, true, false, false);
    //            accum.append('"');
    //        }
    //    }
    //
    //    protected void html(Appendable accum, Document.OutputSettings out) throws IOException {
    //        html(key, val, accum, out);
    //    }
    //    /**
    //     Get the string representation of this attribute, implemented as {@link #html()}.
    //     @return string
    //     */
    //    @Override
    //    public String toString() {
    //        return html();
    //    }
    //    /**
    //     * Create a new Attribute from an unencoded key and a HTML attribute encoded value.
    //     * @param unencodedKey assumes the key is not encoded, as can be only run of simple \w chars.
    //     * @param encodedValue HTML attribute encoded value
    //     * @return attribute
    //     */
    //    public static Attribute createFromEncoded(String unencodedKey, String encodedValue) {
    //        String value = Entities.unescape(encodedValue, true);
    //        return new Attribute(unencodedKey, value, null); // parent will get set when Put
    //    }
    //    protected boolean isDataAttribute() {
    //        return isDataAttribute(key);
    //    }
    //
    //    protected static boolean isDataAttribute(String key) {
    //        return key.startsWith(Attributes.dataPrefix) && key.length() > Attributes.dataPrefix.length();
    //    }
    //    /**
    //     * Collapsible if it's a boolean attribute and value is empty or same as name
    //     *
    //     * @param out output settings
    //     * @return  Returns whether collapsible or not
    //     */
    //    protected final boolean shouldCollapseAttribute(Document.OutputSettings out) {
    //        return shouldCollapseAttribute(key, val, out);
    //    }
    //    protected static boolean shouldCollapseAttribute(final String key, final String val, final Document.OutputSettings out) {
    //        return (
    //                out.syntax() == Document.OutputSettings.Syntax.html &&
    //                        (val == null || ("".equals(val) || val.equalsIgnoreCase(key)) && Attribute.isBooleanAttribute(key)));
    //    }
    //    /**
    //     * @deprecated
    //     */
    //    protected boolean isBooleanAttribute() {
    //        return Arrays.binarySearch(booleanAttributes, key) >= 0 || val == null;
    //    }
    //
    //    /**
    //     * Checks if this attribute name is defined as a boolean attribute in HTML5
    //     */
    //    protected static boolean isBooleanAttribute(final String key) {
    //        return Arrays.binarySearch(booleanAttributes, key) >= 0;
    //    }
    override fun equals(other: Any?): Boolean { // note parent not considered
        if (this === other) return true
        if (other == null || javaClass != other.javaClass) return false
        val (key1, value1) = other as Attribute
        if (if (keyEntry != null) keyEntry != key1 else key1 != null) return false
        return if (valueEntry != null) valueEntry == value1 else value1 == null
    }

    override fun hashCode(): Int { // note parent not considered
        var result = if (keyEntry != null) keyEntry.hashCode() else 0
        result = 31 * result + if (valueEntry != null) valueEntry.hashCode() else 0
        return result
    }

    public override fun clone(): Attribute {
        return try {
            super.clone() as Attribute
        } catch (e: CloneNotSupportedException) {
            throw RuntimeException(e)
        }
    }


}